/**   
* @{#} Filter.java Create on 2013-6-22 下午4:20:20   
*   
* Copyright (c) 2012 by Baidu.   
*/
package objtable.algm.extend;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import objtable.algm.Algm1Row;
import objtable.struct.Row;

/**   
* @author<a href="mailto:zhangbo07@baidu.com">zhangbo07</a>  
* @version 1.0 
* <p>带索引的过滤器，第一次查询数据的时候会根据之前的表达式构造索引
* TableCenter判断使用相同索引过滤器的条件是：1 表名相同  2 解析出来的field字段相同  
* 表达式必须使用==条件，而且必须是&&关系才能使用索引过滤器</p>
*/
public class IndexedFilterBy implements Algm1Row {
	private String newExpress;
	private List<Row> rows;
	private List<FieldValue> fields;
	public static Pattern P_FILTERBY = Pattern
			.compile("\\s*(\\w*)\\s*==\\s*(\\w*)\\s*");
	private HashMap<String, List<Row>> hm_valuekey;

	/**
	 * 表达式必须使用==条件，而且必须是&&关系才能使用索引过滤器
	 * @param tablename
	 * @param simpleExpress 简单表达式，目前只能对 field==** && field==**这种结构做索引
	 */
	public IndexedFilterBy(String simpleExpress, List<Row> rs) {
		rows = rs;
		fields = parseExpress(simpleExpress);
	}

	public void setNewExpress(String exp) {
		this.newExpress = exp;
	}

	/**
	 * 第一次查询数据的时候构造索引
	 */
	private void makeIndex() {
		if (hm_valuekey == null) {
			hm_valuekey = new HashMap<String, List<Row>>();
			for (Row r : rows) {
				//根据初始化时候传入的表达式中的filed构造联合索引，将Row对象相应字段的值拿出来构造fieldvalue1^fieldvalue2结构的联合索引
				String key = getValueKey(r);

				if (!hm_valuekey.containsKey(key)) {
					ArrayList<Row> al = new ArrayList<Row>();
					hm_valuekey.put(key, al);
				}

				hm_valuekey.get(key).add(r);
			}
		}
	}

	/**
	 * 解析简单表达式，返回排序后的解析结果，&&左右顺序调整被认为是同一表达式
	 * @param simpleExpress
	 * @return
	 */
	private List<FieldValue> parseExpress(String simpleExpress) {
		List<FieldValue> result = new ArrayList<IndexedFilterBy.FieldValue>();
		Matcher m = P_FILTERBY.matcher(simpleExpress);
		while (m.find()) {
			FieldValue fv = new FieldValue(m.group(1), m.group(2));
			result.add(fv);
		}
		Collections.sort(result);
		return result;
	}

	/**
	 * 获取表达式解析出来的field串，形式类似field1field2
	 * TableCenter使用这个串来判断是否使用同一个已经索引过的Fileter来获取数据
	 * @return
	 */
	public String getCacheKey() {
		String result = "";
		for (FieldValue fv : fields) {
			result += (fv.fieldname + Row.SPLIT_CHAR);
		}
		return rows + result;
	}

	/**
	 * 根据传入的表达式解析结果，获取表达式中的value键值
	 * @param l
	 * @return
	 */
	private String getValueKey(List<FieldValue> l) {
		String result = "";
		for (FieldValue fv : l) {
			result += (fv.fieldvalue + Row.SPLIT_CHAR);
		}
		return result;
	}

	/**
	 * 根据传入的row对象和之前构造函数中的表达式解析结果，获得相应field的value键值
	 * @param r
	 * @return
	 */
	private String getValueKey(Row r) {
		String result = "";
		for (FieldValue fv : fields) {
			result += (r.get(fv.fieldname).toString() + Row.SPLIT_CHAR);
		}
		return result;
	}

	/**
	 * 根据传入的表达式，获取已经构造好的索引记录
	 * @param express
	 * @return
	 */
	public List<Row> getResult(String express) {
		makeIndex();
		List<FieldValue> l = parseExpress(express);
		List<Row> result = null;
		if (fieldKeyEquals(l)) {
			result = hm_valuekey.get(getValueKey(l));
		}
		return result == null ? new ArrayList<Row>() : result;
	}

	/**
	 * 确认新传入的表达式字段和原来构造函数的表达式字段相同
	 * @param l
	 * @return
	 */
	private boolean fieldKeyEquals(List<FieldValue> l) {
		if (fields.size() != l.size()) {
			return false;
		}

		for (int i = 0; i < fields.size(); i++) {
			if (!fields.get(i).fieldname.equals(l.get(i).fieldname)) {
				return false;
			}
		}

		return true;
	}

	class FieldValue implements Comparable<FieldValue> {
		public String fieldname;
		public String fieldvalue;

		public FieldValue(String name, String value) {
			this.fieldname = name;
			this.fieldvalue = value;
		}

		public int compareTo(FieldValue o) {
			return this.fieldname.compareTo(o.fieldname);
		}
	}

	public List<? extends Row> execute(List<? extends Row> rows) {
		makeIndex();
		List<Row> result = null;
		List<FieldValue> l = parseExpress(newExpress);
		if (rows == this.rows && rows.size() == this.rows.size()
				&& fieldKeyEquals(l)) {
			result = hm_valuekey.get(getValueKey(l));
		}
		return result == null ? new ArrayList<Row>() : result;
	}
}
